//
// Copyright (c) 2012, Tomoto S. Washio
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//   * Neither the name of the Tomoto S. Washio nor the names of his
//     contributors may be used to endorse or promote products derived from
//     this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL TOMOTO S. WASHIO BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
package com.tomoto.charset.native2ascii;

import static com.tomoto.charset.native2ascii.Native2AsciiCharsetUtils.BACKSLASH_BYTE;
import static com.tomoto.charset.native2ascii.Native2AsciiCharsetUtils.U_BYTE;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;

public class Native2AsciiCharsetDecoder extends CharsetDecoder {

	protected Native2AsciiCharsetDecoder() {
		super(Native2AsciiCharsetUtils.N2A_CHARSET, 1, 6);
	}
	
	@Override
	protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
		CoderResult result = null;
		while (in.hasRemaining()) {
			byte b = in.get(in.position()); // lookahead
			result = (b != BACKSLASH_BYTE) ? copy(in, out, 1) : processUnicodeEscapeSequence(in, out);
			
			if (result != null) {
				return result;
			}
		}
		return CoderResult.UNDERFLOW;
	}
	
	private CoderResult processUnicodeEscapeSequence(ByteBuffer in, CharBuffer out) {
		assert in.remaining() >= 1;
		
		int o = in.position();
		assert in.get(o) == BACKSLASH_BYTE;
		
		if (in.remaining() < 2) {
			return CoderResult.UNDERFLOW;
		}
		byte u = in.get(o + 1); // lookahead 2nd char
		return (u != U_BYTE) ? copy(in, out, 1) : processUnicoeEscapeSequenceDigits(in, out);
	}

	private CoderResult processUnicoeEscapeSequenceDigits(ByteBuffer in, CharBuffer out) {
		assert in.remaining() >= 2;
		
		int o = in.position();
		assert in.get(o + 1) == U_BYTE;
		
		char c = 0;
		for (int i = 0; i < 4; i++) {
			if (in.remaining() < 3 + i) {
				return CoderResult.UNDERFLOW;
			}
			int d = Character.digit(in.get(o + 2 + i), 16);
			if (d < 0) {
				return copy(in, out, 2 + i);
			}
			c = (char) (c * 16 + d);
		}
		
		if (out.hasRemaining()) {
			out.put(c);
		} else {
			return CoderResult.OVERFLOW;
		}
		
		in.position(in.position() + 6); // finally consume
		return null;
	}

	private CoderResult copy(ByteBuffer in, CharBuffer out, int length) {
		CoderResult result;
		int actualLength;
		
		if (length <= in.remaining() && length <= out.remaining()) {
			actualLength = length;
			result = null;
		} else {
			if (in.remaining() <= out.remaining()) {
				actualLength = in.remaining();
				result = CoderResult.UNDERFLOW;
			} else {
				actualLength = out.remaining();
				result = CoderResult.OVERFLOW;
			}
		}
		
		while (actualLength-- > 0) {
			char c = (char) (in.get() & 0xff);
			out.put(c);
		}
		
		return result;
	}
}
